home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / tnos / tnos100s / mbuf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-27  |  13.4 KB  |  621 lines

  1. /* mbuf (message buffer) primitives
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  *
  4.  * Mods by G1EMM
  5.  */
  6.  
  7. #include <stdio.h>
  8. #include <dos.h>    /* TEMP */
  9. #include "global.h"
  10. #include <alloc.h>
  11. #include "mbuf.h"
  12. #include "proc.h"
  13. #include "config.h"
  14.  
  15. #ifndef TNOS_68K
  16. /* Interrupt buffer pool */
  17. int Intqlen;            /* Number of free mbufs on Intq */
  18. struct mbuf *Intq;        /* Mbuf pool for interrupt handlers */
  19. struct mbuf *Garbq;        /* List of buffers freed at interrupt time */
  20. long Ibuffail;            /* Allocate failures */
  21. int Iminfree  = -1;        /* minimum free buffers */
  22.  
  23. void
  24. refiq()
  25. {
  26.     register struct mbuf *bp;
  27.     char i_state;
  28. #ifdef    PI        /* Temp hack to satisfy PI DMA requirements */
  29.     int32 dma_abs;    /* TEMP */
  30.     int16 dma_page;    /* TEMP */
  31. #endif
  32.  
  33.     /* Empty the garbage */
  34.     if(Garbq != NULLBUF){
  35.         i_state = dirps();
  36.         bp = Garbq;
  37.         Garbq = NULLBUF;
  38.         restore(i_state);
  39.         free_p(bp);
  40.     }
  41.     /* Replenish interrupt buffer pool */ /* G1EMM and HB9RWM fix */
  42.     while((Intqlen < Nibufs) && (Memthresh < availmem()) ){
  43. #ifdef notdef
  44.     while(Intqlen < Nibufs){
  45. #endif
  46.         if((bp = alloc_mbuf(Ibufsize)) == NULLBUF)
  47.             break;
  48. #ifdef    PI        /* Temp hack to satisfy PI DMA requirements */
  49.         dma_abs = ((long)FP_SEG(bp->data) << 4) + (long)FP_OFF(bp->data);
  50.         dma_page = dma_abs >> 16;
  51.         if(((dma_abs+Ibufsize) >> 16) != dma_page){
  52.             i_state = dirps();
  53.             bp->next = Garbq;
  54.             Garbq = bp;
  55.             restore(i_state);
  56.             continue;
  57.         }
  58. #endif
  59.  
  60.         i_state = dirps();
  61.         bp->next = Intq;
  62.         Intq = bp;
  63.         Intqlen++;
  64.         restore(i_state);
  65.     }
  66.     if(Iminfree == -1)
  67.         Iminfree = Intqlen;
  68. }
  69.  
  70. void
  71. iqstat()
  72. {
  73.     tprintf("Intqlen %u Ibufsize %u Iminfree %u Ibuffail %lu\n",
  74.         Intqlen,Ibufsize,Iminfree,Ibuffail);
  75. }
  76.  
  77. void
  78. iqclear()
  79. {
  80.     Ibuffail = 0;
  81.     Iminfree = -1;
  82. }
  83. #endif
  84.  
  85. /* Allocate mbuf with associated buffer of 'size' bytes. If interrupts
  86.  * are enabled, use the regular heap. If they're off, use the special
  87.  * interrupt buffer pool.
  88.  */
  89. struct mbuf *
  90. alloc_mbuf(size)
  91. register int16 size;
  92. {
  93.     register struct mbuf *bp;
  94.  
  95.     if(istate()){
  96.         /* Interrupts are enabled, use the heap normally */
  97.         bp = (struct mbuf *)malloc((unsigned)(size + sizeof(struct mbuf)));
  98.         if(bp == NULLBUF)
  99.             return NULLBUF;
  100.         /* Clear just the header portion */
  101.         memset((char *)bp,0,sizeof(struct mbuf));
  102.         if((bp->size = size) != 0)
  103.             bp->data = (unsigned char *)(bp + 1);
  104.         bp->refcnt++;
  105.     }
  106. #ifndef TNOS_68K
  107.     else {
  108.         /* Interrupts are off, use special interrupt buffer pool */
  109.         if(size > Ibufsize || Intq == NULLBUF){
  110.             Ibuffail++;
  111.             return NULLBUF;
  112.         }
  113.         bp = Intq;
  114.         Intq = bp->next;
  115.         bp->next = NULLBUF;
  116.         Intqlen--;
  117.     }
  118.     if(Intqlen < Iminfree)
  119.         Iminfree = Intqlen;
  120. #endif
  121.     return bp;
  122. }
  123. /* Allocate mbuf, waiting if memory is unavailable */
  124. struct mbuf *
  125. ambufw(size)
  126. int16 size;
  127. {
  128.     register struct mbuf *bp;
  129.  
  130.     bp = (struct mbuf *)mallocw((unsigned)(size + sizeof(struct mbuf)));
  131.  
  132.     /* Clear just the header portion */
  133.     memset((char *)bp,0,sizeof(struct mbuf));
  134.     if((bp->size = size) != 0)
  135.         bp->data = (unsigned char *)(bp + 1);
  136.     bp->refcnt++;
  137.     return bp;
  138. }
  139.  
  140. /* Decrement the reference pointer in an mbuf. If it goes to zero,
  141.  * free all resources associated with mbuf.
  142.  * Return pointer to next mbuf in packet chain
  143.  */
  144. struct mbuf *
  145. free_mbuf(bp)
  146. register struct mbuf *bp;
  147. {
  148.     struct mbuf *bpnext;
  149.  
  150.     if(bp == NULLBUF)
  151.         return NULLBUF;
  152.  
  153.     bpnext = bp->next;
  154.     if(bp->dup != NULLBUF){
  155.         free_mbuf(bp->dup);    /* Follow indirection */
  156.         bp->dup = NULLBUF;
  157.     }
  158.     /* Decrement reference count. If it has gone to zero, free it. */
  159.     if(--bp->refcnt <= 0){
  160.         if(istate()){
  161.             free((char *)bp);
  162.         } else {
  163. #ifndef TNOS_68K
  164.             /* If the interrupt pool isn't full and this buffer
  165.              * appears to have come from it, put it back.
  166.              * Otherwise put it on the garbage list where it
  167.              * will be freed by refiq() later with interrupts
  168.              * enabled.
  169.              *
  170.              * This test handles the common special case of
  171.              * an interrupt handler allocating a buffer and
  172.              * then freeing it before returning (e.g., due to
  173.              * a receive abort or CRC failure).
  174.              */
  175.             bp->refcnt = 1;    /* Adjust */
  176.             if(bp->size == Ibufsize && Intqlen < Nibufs){
  177.                 bp->next = Intq;
  178.                 bp->anext = NULLBUF;
  179.                 bp->data = (unsigned char *)(bp + 1);
  180.                 bp->cnt = 0;
  181.                 Intq = bp;
  182.                 Intqlen++;
  183.             } else {
  184.                 bp->next = Garbq;
  185.                 Garbq = bp;
  186.             }
  187. #endif
  188.         }
  189.     }
  190.     return bpnext;
  191. }
  192.  
  193. /* Free packet (a chain of mbufs). Return pointer to next packet on queue,
  194.  * if any
  195.  */
  196. struct mbuf *
  197. free_p(bp)
  198. register struct mbuf *bp;
  199. {
  200.     register struct mbuf *abp;
  201.  
  202.     if(bp == NULLBUF)
  203.         return NULLBUF;
  204.     abp = bp->anext;
  205.     while(bp != NULLBUF)
  206.         bp = free_mbuf(bp);
  207.     return abp;
  208. }        
  209. /* Free entire queue of packets (of mbufs) */
  210. void
  211. free_q(q)
  212. struct mbuf **q;
  213. {
  214.     register struct mbuf *bp;
  215.  
  216.     while((bp = dequeue(q)) != NULLBUF)
  217.         free_p(bp);
  218. }
  219.  
  220. /* Count up the total number of bytes in a packet */
  221. int16
  222. len_p(bp)
  223. register struct mbuf *bp;
  224. {
  225.     register int16 cnt = 0;
  226.  
  227.     while(bp != NULLBUF){
  228.         cnt += bp->cnt;
  229.         bp = bp->next;
  230.     }
  231.     return cnt;
  232. }
  233. /* Count up the number of packets in a queue */
  234. int16
  235. len_q(bp)
  236. register struct mbuf *bp;
  237. {
  238.     register int16 cnt;
  239.  
  240.     for(cnt=0;bp != NULLBUF;cnt++,bp = bp->anext)
  241.         ;
  242.     return cnt;
  243. }
  244. /* Trim mbuf to specified length by lopping off end */
  245. void
  246. trim_mbuf(bpp,length)
  247. struct mbuf **bpp;
  248. int16 length;
  249. {
  250.     register int16 tot = 0;
  251.     register struct mbuf *bp;
  252.  
  253.     if(bpp == NULLBUFP || *bpp == NULLBUF)
  254.         return;    /* Nothing to trim */
  255.  
  256.     if(length == 0){
  257.         /* Toss the whole thing */
  258.         free_p(*bpp);
  259.         *bpp = NULLBUF;
  260.         return;
  261.     }
  262.     /* Find the point at which to trim. If length is greater than
  263.      * the packet, we'll just fall through without doing anything
  264.      */
  265.     for( bp = *bpp; bp != NULLBUF; bp = bp->next){
  266.         if(tot + bp->cnt < length){
  267.             tot += bp->cnt;
  268.         } else {
  269.             /* Cut here */
  270.             bp->cnt = length - tot;
  271.             free_p(bp->next);
  272.             bp->next = NULLBUF;
  273.             break;
  274.         }
  275.     }
  276. }
  277. /* Duplicate/enqueue/dequeue operations based on mbufs */
  278.  
  279. /* Duplicate first 'cnt' bytes of packet starting at 'offset'.
  280.  * This is done without copying data; only the headers are duplicated,
  281.  * but without data segments of their own. The pointers are set up to
  282.  * share the data segments of the original copy. The return pointer is
  283.  * passed back through the first argument, and the return value is the
  284.  * number of bytes actually duplicated.
  285.  */
  286. int16
  287. dup_p(hp,bp,offset,cnt)
  288. struct mbuf **hp;
  289. register struct mbuf *bp;
  290. register int16 offset;
  291. register int16 cnt;
  292. {
  293.     register struct mbuf *cp;
  294.     int16 tot;
  295.  
  296.     if(cnt == 0 || bp == NULLBUF || hp == NULLBUFP){
  297.         if(hp != NULLBUFP)
  298.             *hp = NULLBUF;
  299.         return 0;
  300.     }
  301.     if((*hp = cp = alloc_mbuf(0)) == NULLBUF){
  302.         return 0;
  303.     }
  304.     /* Skip over leading mbufs that are smaller than the offset */
  305.     while(bp != NULLBUF && bp->cnt <= offset){
  306.         offset -= bp->cnt;
  307.         bp = bp->next;
  308.     }
  309.     if(bp == NULLBUF){
  310.         free_mbuf(cp);
  311.         *hp = NULLBUF;
  312.         return 0;    /* Offset was too big */
  313.     }
  314.     tot = 0;
  315.     for(;;){
  316.         /* Make sure we get the original, "real" buffer (i.e. handle the
  317.          * case of duping a dupe)
  318.          */
  319.         if(bp->dup != NULLBUF)
  320.             cp->dup = bp->dup;
  321.         else
  322.             cp->dup = bp;
  323.  
  324.         /* Increment the duplicated buffer's reference count */
  325.         cp->dup->refcnt++;
  326.  
  327.         cp->data = bp->data + offset;
  328.         cp->cnt = min(cnt,bp->cnt - offset);
  329.         offset = 0;
  330.         cnt -= cp->cnt;
  331.         tot += cp->cnt;
  332.         bp = bp->next;
  333.         if(cnt == 0 || bp == NULLBUF || (cp->next = alloc_mbuf(0)) == NULLBUF)
  334.             break;
  335.         cp = cp->next;
  336.     }
  337.     return tot;
  338. }
  339. /* Copy first 'cnt' bytes of packet into a new, single mbuf */
  340. struct mbuf *
  341. copy_p(bp,cnt)
  342. register struct mbuf *bp;
  343. register int16 cnt;
  344. {
  345.     register struct mbuf *cp;
  346.     register unsigned char *wp;
  347.     register int16 n;
  348.  
  349.     if(bp == NULLBUF || cnt == 0 || (cp = alloc_mbuf(cnt)) == NULLBUF)
  350.         return NULLBUF;
  351.     wp = cp->data;
  352.     while(cnt != 0 && bp != NULLBUF){
  353.         n = min(cnt,bp->cnt);
  354.         memcpy(wp,bp->data,n);
  355.         wp += n;
  356.         cp->cnt += n;
  357.         cnt -= n;
  358.         bp = bp->next;
  359.     }
  360.     return cp;
  361. }
  362. /* Copy and delete "cnt" bytes from beginning of packet. Return number of
  363.  * bytes actually pulled off
  364.  */
  365. int16
  366. pullup(bph,buf,cnt)
  367. struct mbuf **bph;
  368. unsigned char *buf;
  369. int16 cnt;
  370. {
  371.     register struct mbuf *bp;
  372.     int16 n,tot;
  373.  
  374.     tot = 0;
  375.     if(bph == NULLBUFP)
  376.         return 0;
  377.     while(cnt != 0 && (bp = *bph) != NULLBUF){
  378.         n = min(cnt,bp->cnt);
  379.         if(buf != NULLCHAR){
  380.             if(n == 1)    /* Common case optimization */
  381.                 *buf = *bp->data;
  382.             else if(n > 1)
  383.                 memcpy(buf,bp->data,n);
  384.             buf += n;
  385.         }
  386.         tot += n;
  387.         cnt -= n;
  388.         bp->data += n;
  389.         bp->cnt -= n;        
  390.         if(bp->cnt == 0){
  391.             /* If this is the last mbuf of a packet but there
  392.              * are others on the queue, return a pointer to
  393.              * the next on the queue. This allows pullups to
  394.              * to work on a packet queue
  395.              */
  396.             if(bp->next == NULLBUF && bp->anext != NULLBUF){
  397.                 *bph = bp->anext;
  398.                 free_mbuf(bp);
  399.             } else
  400.                 *bph = free_mbuf(bp);
  401.         }
  402.     }
  403.     return tot;
  404. }
  405. /* Append mbuf to end of mbuf chain */
  406. void
  407. append(bph,bp)
  408. struct mbuf **bph;
  409. struct mbuf *bp;
  410. {
  411.     register struct mbuf *p;
  412.  
  413.     if(bph == NULLBUFP || bp == NULLBUF)
  414.         return;
  415.     if(*bph == NULLBUF){
  416.         /* First one on chain */
  417.         *bph = bp;
  418.     } else {
  419.         for(p = *bph ; p->next != NULLBUF ; p = p->next)
  420.             ;
  421.         p->next = bp;
  422.     }
  423. }
  424. /* Insert specified amount of contiguous new space at the beginning of an
  425.  * mbuf chain. If enough space is available in the first mbuf, no new space
  426.  * is allocated. Otherwise a mbuf of the appropriate size is allocated and
  427.  * tacked on the front of the chain.
  428.  *
  429.  * This operation is the logical inverse of pullup(), hence the name.
  430.  */
  431. struct mbuf *
  432. pushdown(bp,size)
  433. register struct mbuf *bp;
  434. int16 size;
  435. {
  436.     register struct mbuf *nbp;
  437.  
  438.     /* Check that bp is real, that it hasn't been duplicated, and
  439.      * that it itself isn't a duplicate before checking to see if
  440.      * there's enough space at its front.
  441.      */
  442.     if(bp != NULLBUF && bp->refcnt == 1 && bp->dup == NULLBUF
  443.      && bp->data - (unsigned char *)(bp+1) >= size){
  444.         /* No need to alloc new mbuf, just adjust this one */
  445.         bp->data -= size;
  446.         bp->cnt += size;
  447.     } else {
  448.         nbp = ambufw(size);
  449.         nbp->next = bp;
  450.         nbp->cnt = size;
  451.         bp = nbp;
  452.     }
  453.     return bp;
  454. }
  455. /* Append packet to end of packet queue */
  456. void
  457. enqueue(q,bp)
  458. struct mbuf **q;
  459. struct mbuf *bp;
  460. {
  461.     register struct mbuf *p;
  462.     char i_state;
  463.  
  464.     if(q == NULLBUFP || bp == NULLBUF)
  465.         return;
  466.     i_state = dirps();
  467.     if(*q == NULLBUF){
  468.         /* List is empty, stick at front */
  469.         *q = bp;
  470.     } else {
  471.         for(p = *q ; p->anext != NULLBUF ; p = p->anext)
  472.             ;
  473.         p->anext = bp;
  474.     }
  475.     restore(i_state);
  476.     psignal(q,1);
  477. }
  478. /* Unlink a packet from the head of the queue */
  479. struct mbuf *
  480. dequeue(q)
  481. register struct mbuf **q;
  482. {
  483.     register struct mbuf *bp;
  484.     char i_state;
  485.  
  486.     if(q == NULLBUFP)
  487.         return NULLBUF;
  488.     i_state = dirps();
  489.     if((bp = *q) != NULLBUF){
  490.         *q = bp->anext;
  491.         bp->anext = NULLBUF;
  492.     }
  493.     restore(i_state);
  494.     return bp;
  495. }    
  496.  
  497. /* Copy user data into an mbuf */
  498. struct mbuf *
  499. qdata(data,cnt)
  500. unsigned char *data;
  501. int16 cnt;
  502. {
  503.     register struct mbuf *bp;
  504.  
  505.     bp = ambufw(cnt);
  506.     memcpy(bp->data,data,cnt);
  507.     bp->cnt = cnt;
  508.     return bp;
  509. }
  510. /* Copy mbuf data into user buffer */
  511. int16
  512. dqdata(bp,buf,cnt)
  513. struct mbuf *bp;
  514. unsigned char *buf;
  515. unsigned cnt;
  516. {
  517.     int16 tot;
  518.     unsigned n;
  519.     struct mbuf *bp1;
  520.  
  521.     if(buf == NULLCHAR)
  522.         return 0;
  523.     
  524.     tot = 0;
  525.     for(bp1 = bp;bp1 != NULLBUF; bp1 = bp1->next){
  526.         n = min(bp1->cnt,cnt);
  527.         memcpy(buf,bp1->data,n);
  528.         cnt -= n;
  529.         buf += n;
  530.         tot += n;
  531.     }
  532.     free_p(bp);
  533.     return tot;
  534. }
  535. /* Pull a 32-bit integer in host order from buffer in network byte order.
  536.  * On error, return 0. Note that this is indistinguishable from a normal
  537.  * return.
  538.  */
  539. int32
  540. pull32(bpp)
  541. struct mbuf **bpp;
  542. {
  543.     char buf[4];
  544.  
  545.     if(pullup(bpp,buf,4) != 4){
  546.         /* Return zero if insufficient buffer */
  547.         return 0;
  548.     }
  549.     return get32(buf);
  550. }
  551. /* Pull a 16-bit integer in host order from buffer in network byte order.
  552.  * Return -1 on error
  553.  */
  554. long
  555. pull16(bpp)
  556. struct mbuf **bpp;
  557. {
  558.     char buf[2];
  559.  
  560.     if(pullup(bpp,buf,2) != 2){
  561.         return -1;        /* Nothing left */
  562.     }
  563.     return get16(buf);
  564. }
  565. /* Pull single character from mbuf */
  566. int
  567. pullchar(bpp)
  568. struct mbuf **bpp;
  569. {
  570.     char c;
  571.  
  572.     if(pullup(bpp,&c,1) != 1)
  573.         return -1;        /* Nothing left */
  574.     return (int)uchar(c);
  575. }
  576. int
  577. write_p(fp,bp)
  578. FILE *fp;
  579. struct mbuf *bp;
  580. {
  581.     while(bp != NULLBUF){
  582.         if(fwrite(bp->data,1,bp->cnt,fp) != bp->cnt)
  583.             return -1;
  584.         bp = bp->next;
  585.     }
  586.     return 0;
  587. }
  588. /* Reclaim unused space in a mbuf chain. If the argument is a chain of mbufs
  589.  * and/or it appears to have wasted space, copy it to a single new mbuf and
  590.  * free the old mbuf(s). But refuse to move mbufs that merely
  591.  * reference other mbufs, or that have other headers referencing them.
  592.  *
  593.  * Be extremely careful that there aren't any other pointers to
  594.  * (or into) this mbuf, since we have no way of detecting them here.
  595.  * This function is meant to be called only when free memory is in
  596.  * short supply.
  597.  */
  598. void
  599. mbuf_crunch(bpp)
  600. struct mbuf **bpp;
  601. {
  602.     register struct mbuf *bp = *bpp;
  603.     struct mbuf *nbp;
  604.  
  605.     if(bp->refcnt > 1 || bp->dup != NULLBUF){
  606.         /* Can't crunch, there are other refs */
  607.         return;
  608.     }
  609.     if(bp->next == NULLBUF && bp->cnt == bp->size){
  610.         /* Nothing to be gained by crunching */
  611.         return;
  612.     }
  613.     if((nbp = copy_p(bp,len_p(bp))) == NULLBUF){
  614.         /* Copy failed due to lack of (contiguous) space */
  615.         return;
  616.     }
  617.     nbp->anext = bp->anext;
  618.     free_p(bp);
  619.     *bpp = nbp;
  620. }
  621.